package org.jeecg.modules.system.filter;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.javafilter.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.javafilter.authentication.GatewayResolver;
import org.jasig.cas.client.javafilter.util.AbstractCasFilter;
import org.jasig.cas.client.javafilter.util.CommonUtils;
import org.jasig.cas.client.javafilter.validation.Assertion;

import com.sgcc.isc.ualogin.client.util.IscWhitePageUtil;
import com.sgcc.isc.ualogin.client.util.JProperties;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyAuthenticationFilter extends AbstractCasFilter {
	private String casServerLoginUrl;
	private boolean renew = false;

	private boolean gateway = false;

	private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
	private List<String> exceptUris;
	private List<String> timeoutToHomeSuffixs;

	protected void initInternal(FilterConfig filterConfig) throws ServletException {
		if (!isIgnoreInitConfiguration()) {
			super.initInternal(filterConfig);
			setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
			log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
			setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
			log.trace("Loaded renew parameter: " + this.renew);
			setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
			log.trace("Loaded gateway parameter: " + this.gateway);

			String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

			if (gatewayStorageClass != null)
				try {
					this.gatewayStorage = ((GatewayResolver) Class.forName(gatewayStorageClass).newInstance());
				} catch (Exception e) {
					log.error(e.getMessage(), e);
					throw new ServletException(e);
				}
		}
	}

	public void init() {
		super.init();
		CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
		JProperties properties = new JProperties("etxuri.properties");
		String etxuri = properties.getValue("etxuri");
		String[] etxuris = etxuri.split("&");
		etxuris = etxuris == null ? new String[0] : etxuris;
		this.exceptUris = new LinkedList();
		for (String uri : etxuris) {
			this.exceptUris.add(uri);
		}

		String timeoutToHomeSuffix = properties.getValue("timeoutToHomeSuffix");
		if ((timeoutToHomeSuffix != null) && (!"".equals(timeoutToHomeSuffix))) {
			String[] toHomeSuffixs = timeoutToHomeSuffix.split("&");
			toHomeSuffixs = toHomeSuffixs == null ? new String[0] : toHomeSuffixs;
			this.timeoutToHomeSuffixs = new LinkedList();
			for (String suffix : toHomeSuffixs)
				this.timeoutToHomeSuffixs.add(suffix);
		} else {
			this.timeoutToHomeSuffixs = new LinkedList();
		}
	}

	protected boolean routeToLocal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		return false;
	}

	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		HttpSession session = request.getSession(false);

		if (IscWhitePageUtil.isExceptUri(request, this.exceptUris)) {
			filterChain.doFilter(request, response);
			return;
		}
		if (routeToLocal(servletRequest, servletResponse, filterChain)) {
			return;
		}

		Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;

		if (assertion != null) {
			filterChain.doFilter(request, response);
			return;
		}

		String serviceUrl = constructServiceUrl(request, response);
		String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
		boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

		if ((CommonUtils.isNotBlank(ticket)) || (wasGatewayed)) {
			filterChain.doFilter(request, response);
			return;
		}

		for (int i = 0; i < this.timeoutToHomeSuffixs.size(); i++) {
			String suffix = (String) this.timeoutToHomeSuffixs.get(i);
			if ((serviceUrl != null) && (serviceUrl.endsWith(suffix))) {
				serviceUrl = serviceUrl.replace(suffix, "");
				break;
			}
		}

		log.debug("no ticket and no assertion found");
		String modifiedServiceUrl;
		if (this.gateway) {
			log.debug("setting gateway attribute in session");
			modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
		} else {
			modifiedServiceUrl = serviceUrl;
		}

		if (log.isDebugEnabled()) {
			log.debug("Constructed service url: " + modifiedServiceUrl);
		}

		String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

		if (log.isDebugEnabled()) {
			log.debug("redirecting to \"" + urlToRedirectTo + "\"");
		}
		Cookie[] cookies = request.getCookies();
		if ((cookies != null) && (cookies.length > 0)) {
			for (int i = 0; i < cookies.length; i++) {
				cookies[i].setMaxAge(0);
				cookies[i].setPath("/");
				response.addCookie(cookies[i]);
			}
		}

		log.error("MyAuthenticationFilter 401 error");
		response.setStatus(401);
		// response.sendRedirect(urlToRedirectTo);
	}

	public final void setRenew(boolean renew) {
		this.renew = renew;
	}

	public final void setGateway(boolean gateway) {
		this.gateway = gateway;
	}

	public final void setCasServerLoginUrl(String casServerLoginUrl) {
		this.casServerLoginUrl = casServerLoginUrl;
	}

	public final void setGatewayStorage(GatewayResolver gatewayStorage) {
		this.gatewayStorage = gatewayStorage;
	}
}